迭代器的使用

当你第一次遇到某个迭代器时,总能得到几个引用着最有用的元素的迭代器:begin()end() 是其中最好的例子。除此之外,许多算法也将返回迭代器。例如,标准算法 find 在一个序列里查找某个值,并返回引用着找到的那个元素的迭代器。利用 find,我们可以统计出某一字符在一个 string 中出现的次数:

    int count(const string& s, char c)
    {
        int n = 0;
        string::const_iterator i = find(s.begin(), s.end(), c);
        while(i!=s.end())
        {
            ++n;
            i = find(i+1, s.end(), c);
        }
        return n;
    }

算法 find 返回一个引用着有关值在序列里的第一次出现的迭代器,或者是超过结束处一个位置的迭代器。现在考虑对 count 的简单调用中会出现什么情况:

    void f()
    {
        string m = "Mary had a little lamb";
        int a_count = count(m, 'a');
    }

find() 的第一次调用将发现 Mary 里的 'a'。这时返回的迭代器将指向这个字符,因而不是 s.end(),所以我们就进入了循环。在循环中,我们又从 i+1 开始检索;也就是说,从所发现的 'a' 之后一个位置开始。随后的循环发现了另外三个 'a'。完成了所有这些之后,find() 到达了结束处并返回 s.end(),这使条件 i!=s.end() 为假,并使循环退出。

这里对 count() 的调用可以用图形方式表示如下

这里的箭头表示的是迭代器 i 的初始的、中间的和最后的位置。

很自然,算法 find 对于每个标准容器的工作都完全相同。根据这个情况,我们也可以按照同样的方式推广 count() 函数:

    template<class C, class T> int count(const C& v, T val)
    {
        typename C::const_iterator i = find(v.begin(), v.end(), val);    //  "typename" 参见C.13.5节
        int n = 0;
        while(i!=v.end())
        {
            ++n;
            ++i;    // 跳过刚刚发现的元素
            i = find(i, v.end(), val);
        }
        return n;
    }

这样做能行,因此我们可以说

    void f(list<complex>& lc, vector<string>& vs, string s)
    {
        int i1 = count(lc, complex(1, 3));
        int i2 = count(vs, "Diogenes");
        int i3 = count(s, 'x');
    }

但是我们并不需要去定义 count 模板。由于统计出元素的出现次数是如此具有一般性的有用功能,标准库提供了这个算法。为了达到完全的通用性,标准库的 count 以一个序列作为参数,而不是以容器作为参数,因此我们应该采用如下的写法:

    void f(list<complex>& lc, vector<string>& vs, string s)
    {
        int i1 = count(lc.begin(), lc.end(), complex(1, 3));
        int i2 = count(vs.begin(), vs.end(), "Diogenes");
        int i3 = count(s.begin(), s.end(), 'x');

    }

采用序列的写法,将使我们也能把 count 用于内部数组,还可以对容器中的一部分做统计。例如,

    void g(char cs[], int sz)
    {
        int i1 = count(&cs[0], &cs[sz], 'z');      // 数组中的'z'
        int i2 = count(&cs[0], &cs[sz/2], 'z');    // 数组前一半中的'z'
    }

🔚